package com.tang.logger.pusher.databases;

import com.tang.logger.config.LogPusherProperties;
import com.tang.logger.domain.LogMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.List;

/**
 * @author Tang
 * @classname JdbcService
 * @description [  ]
 * @date 2021/7/25 11:50
 */
@Slf4j
@Component
public class JdbcService implements InitializingBean {

    @Resource
    DataSource dataSource;

    @Resource
    LogPusherProperties logPusherProperties;

    private String insertSql;

    PreparedStatement preparedStatement;

    Connection connection;

    private static final int MAX_RETRY_COUNT = 5;

    /**
     * single
     *
     * @param logMessage
     */
    public void insert(LogMessage logMessage) {
        try {
            fillPreparedStatement(logMessage);
            preparedStatement.executeUpdate();
            connection.commit();
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
    }

    /**
     * batch
     *
     * @param logMessageList
     */
    public void insertBatch(List<LogMessage> logMessageList) {
        try {
            for (LogMessage logMessage : logMessageList) {
                fillPreparedStatement(logMessage);
                preparedStatement.addBatch();
            }
            preparedStatement.executeBatch();
            preparedStatement.clearBatch();
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
            // 业务重试
            refreshAndRetry(() -> {
                this.insertBatch(logMessageList);
            });
        }
    }

    /**
     * 重试任务执行
     *
     * @param runnable
     */
    private void refreshAndRetry(Runnable runnable){
        boolean connectionValid = false;
        int retryCount = 1;
        try {
            do {
                try {
                    this.refresh();
                } catch (Exception e) {
                    log.error("第{}次重连数据库失败!", retryCount, e);
                }
            } while (retryCount++ <= MAX_RETRY_COUNT && !(connectionValid = connection.isValid(5)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        retryCount = 1;
        if (connectionValid) {
            runnable.run();
        }
    }

    private void refresh() throws Exception {
        connection = dataSource.getConnection();
        connection.setAutoCommit(false);
        preparedStatement = connection.prepareStatement(insertSql);
    }

    /**
     * fill
     *
     * @param logMessage
     * @throws Exception
     */
    public void fillPreparedStatement(LogMessage logMessage) throws Exception {
        preparedStatement.setString(1, logMessage.getMessage());
        preparedStatement.setObject(2, logMessage.getTimestamp());
        preparedStatement.setString(3, logMessage.getThreadName());
        preparedStatement.setString(4, logMessage.getClassName());
        preparedStatement.setString(5, logMessage.getLevel());
        preparedStatement.setString(6, logMessage.getException());
        preparedStatement.setString(7, logMessage.getCause());
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        String templateSql = "insert into %s (message,timestamp,thread_name,class_name,level,exception,cause) values(?,?,?,?,?,?,?)";
        LogPusherProperties.DatabaseProperties database = logPusherProperties.getDatabase();
        String tableName = database.getTableName();
        insertSql = String.format(templateSql, tableName);
        this.refresh();
    }
}